/**
 * 
 */
package jvs.sh;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Set;

import jvs.peer.io.FileTransfer.Monitor;
import jvs.peer.sec.Role;
import jvs.peer.sec.User;
import android.provider.ContactsContract.CommonDataKinds.Note;

/**
 * @author qiangli
 * 
 */
public class CommandInterpreter implements Interpreter {
	private class MyMonitorThread extends Thread {
		private Monitor mon = null;
		final long wait = 5 * 60 * 1000;

		public MyMonitorThread(Monitor mon) {
			this.mon = mon;
		}

		public void run() {
			final long started = System.currentTimeMillis();

			try {
				int cnt = 0;

				while (!mon.isDone()) {
					log("cnt: " + cnt++ + " prog " + mon.getProgress());
					Thread.sleep(1000);

					long elapsed = System.currentTimeMillis() - started;
					if (elapsed > wait) {
						mon.cancel();
						break;
					}
				}
				if (mon.isAborted()) {
					err(mon.getStatus());
				} else {
					ok(mon.getStatus());
				}
			} catch (InterruptedException e) {
				err("Transfer interrupted.");
			}
		}

	}

//	private static String formatSize(long size) {
//		if (size < 0) {
//			return "-";
//		}
//		if (size < 1024) {
//			return size + "B";
//		}
//		size /= 1024;
//		if (size < 1024) {
//			return size + "KB";
//		}
//		size /= 1024;
//		return size + "MB";
//	}

	// private AccessControl acl;

	private Console console;
	private String crlf = "\r\n";

	private String nbsp = "\t";

	private IShell shell;
	
	//private int pagesize = 10;

	public CommandInterpreter() {
	}

	protected void abort() throws Exception {
		shell.abort();
		ok("abort status set");
	}

	protected void ant(String args) throws Exception {
		// shell.ant(buildfile, target, verbose, logger);
	}

	protected void attr(String args) throws Exception {
		String[] pa = split(args, 1);
		File f = shell.resolveFile((pa.length > 0) ? pa[0] : null);
		if (f.exists()) {
			StringBuilder sb = new StringBuilder();
			sb.append("File attributes" + crlf);
			shell.attr(f, sb, crlf);
			sb.append(crlf);
			ok(sb.toString());
		} else {
			err("File does not exist. " + f.getPath());
		}
	}

	protected void bsh(String args, String data) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length == 0 && data == null) {
			err("Missing argument");
			return;
		}
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		if (pa.length > 0) {
			File f = resolveFile(pa[0]);
			shell.bsh(f, bos);
		} else {
			shell.bsh(data, bos);
		}
		ok("bsh" + crlf + bos.toString());
	}

	protected void buzz(String args) throws Exception {
		ok("buzzing...");
		shell.buzz(args);
	}

	protected void bye(String args) throws Exception {
		ok("Bye "+shell.id());
		//
		shell.exit();
	}

	protected void cat(String args) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("File name required.");
		} else {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				try {
					String s = shell.cat(f);
					ok(f.getPath() + crlf + s);
				} catch (IOException e) {
					err("" + e.getMessage());
				}
				return;
			} else {
				err("File does not exist. " + f.getPath());
			}
		}
	}

	protected void cd(String args) throws Exception {
		String[] pa = split(args, 1);
		File f = (pa.length > 0 ? resolveFile(pa[0]) : shell.getHome());
		if (f.isDirectory()) {
			shell.chdir(f);
			ok("" + f.getPath());
		} else {
			err("Not a directory. " + f.getPath());
		}
	}

	private void checksum(String file, String arg) throws Exception {
		File f = resolveFile(file);
		try {
			arg = (arg == null) ? "CRC32" : arg;
			if (f.isDirectory()) {
				File[] fs = f.listFiles();
				boolean first = true;
				int fileCount = 0;
				for (int i = 0; i < fs.length; i++) {
					f = fs[i];
					if (f.isFile()) {
						fileCount++;
						String cs = shell.checksum(f, arg);
						// only print status line once
						if (first) {
							ok("" + arg + " bytes name");
							first = false;
						}
						print("" + cs + " " + f.length() + " " + f.getName());
					}

				}
				if (first) {
					ok("" + arg + " bytes name");
				}
				print("Total: " + fs.length + " files: " + fileCount + crlf);
			} else if (f.isFile()) {
				String cs = shell.checksum(f, arg);
				ok("" + arg + " bytes name" + crlf + cs + " " + f.length()
						+ " " + f.getName());
			}
		} catch (NoSuchAlgorithmException e) {
			err("Algorithm not supported. " + arg);
		}
	}

	protected void cksum(String args) throws Exception {
		String[] pa = split(args, 2);
		switch (pa.length) {
		case 0:
			err("Missing argumebt");
			return;
		case 1:
			checksum(pa[0], null);
			return;
		case 2:
			checksum(pa[0], pa[1]);
			return;
		}
	}

	protected void cp(String args) throws Exception {
		String pa[] = split(args, 2);

		if (pa.length == 2) {
			File fr = resolveFile(pa[0]);
			File to = resolveFile(pa[1]);

			if (!fr.exists()) {
				err("Source file does not exist. " + fr.getPath());
				return;
			}
			if (fr.isDirectory()) {
				err("Directory copy not supported. " + fr.getPath());
				return;
			}
			if (to.isDirectory()) {
				to = new File(to, fr.getName());
			}
			if (to.exists()) {
				err("Destination file exists. " + to.getPath());
				return;
			}

			shell.copy(fr, to);

			ok("File copied. " + fr.getPath());
		} else {
			err("Invalid command.");
		}
	}

	protected void date(String args) {
		String pa[] = split(args, 1);
		if (pa.length == 0) {
			ok("" + new Date());
		} else {
			try {
				ok("" + shell.date(pa[0]));
			} catch (Exception e) {
				err("" + e.getMessage());
			}
		}
	}

	protected void dec(String args) throws Exception {
		String[] pa = split(args, 1);
		String dec = shell.decode(pa[0]);
		ok("" + dec);
	}

	protected void dex(String args) throws Exception {
		shell.dex(args);
	}

	protected void df(String param) throws Exception {
		StringBuilder sb = new StringBuilder();
		sb.append("200 Mount map" + crlf);
		shell.df(sb, crlf);
		print(sb.toString());
	}

	protected void dir(String args) throws Exception {
		String[] pa = split(args, 1);
		File f = resolveFile(pa);
		if (f.isDirectory()) {
			StringBuilder sb = new StringBuilder();
			sb.append("200 Directory listing. " + f.getName() + crlf);
			int cnt = shell.dir(f, sb, crlf);
			sb.append("Total: " + cnt);
			print(sb.toString());
		} else {
			err("Not a directory");
		}
	}

	protected void du(String args) throws Exception {
//		String[] pa = split(args, 1);
//		if (pa.length == 0) {
//			err("Missing argument");
//			return;
//		}
//		final File f = resolveFile(pa[0]);
//		final boolean verbose = shell.isVerbose();
//		if (f.exists()) {
//			final Collector<File> trail = new Collector<File>() {
//				int count;
//
//				public void add(File obj) {
//					count++;
//					if (verbose) {
//						print("> " + obj);
//					}
//				}
//			};
//			//
//			Thread th = new Thread() {
//				public void run() {
//					try {
//						long t = shell.du(f, trail);
//						StringBuilder sb = new StringBuilder();
//						sb.append("200 Total ");
//						sb.append(formatSize(t));
//						sb.append(" (");
//						sb.append(t);
//						sb.append(" bytes) used by ");
//						sb.append(f.getPath());
//						print(sb.toString());
//					} catch (Exception e) {
//						print(e.getMessage());
//					}
//				}
//			};
//			th.start();
//		} else {
//			err("File/directory does not exist. " + f.getPath());
//		}
	}

	protected void echo(String args) {
		ok("" + args);
	}

	protected void edit(String args, String data) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("Filename missing");
		} else {
			String txt = pa.length == 1 ? "" : pa[1];
			if (data != null) {
				txt += crlf + data;
			}
			File f = resolveFile(pa[0]);
			boolean exist = f.exists();
			shell.edit(txt, f);
			if (exist) {
				ok("File overwritten. " + f.getPath()
						+ ". Original file saved as .old");
			} else {
				ok("File created. " + f.getPath());
			}
		}
	}

	protected void enc(String args) throws Exception {
		String[] pa = split(args, 1);
		String enc = shell.encode(pa[0]);
		ok("" + enc);
	}

	protected void env(String param) throws Exception {
		ok("env. " + shell.env());
	}

	protected void err(String msg) {
		console.err(msg + crlf);
	}

	protected void execute(String line, String param, String data)
			throws Exception {
		if (Log.DEBUG) {
			Log.log(this, "execute cmd: " + line + " params: " + param + " last access: ");
		}
		// line not null
		String s = shell.getEnv("crlf");
		crlf = (s == null ? "\r\n" : s);

		int idx = line.lastIndexOf("/");
		String cmd = (idx > 0) ? line.substring(idx + 1) : line;
		//
		shell.setLastAccess(System.currentTimeMillis());
		if (cmd.equals("SHELL")) {
			shell(param);
		} else if (cmd.equals("CONNECT") || cmd.equals("HI")
				|| cmd.equals("HELLO")) {
			connect(param);
		} else if (cmd.equals("MOTD")) {
			motd();
		} else if (cmd.equals("ATTR") || cmd.equals("ATTRIB")
				|| cmd.equals("ATTRIBUTE") || cmd.equals("FILE")) {
			attr(param);
		} else if (cmd.equals("INFO")) {
			info(param);
		} else if (cmd.equals("BUZZ") || cmd.equals("BELL")) {
			buzz(param);
		} else if (cmd.equals("ENV")) {
			env(param);
		} else if (cmd.equals("SET")) {
			set(param);
		} else if (cmd.equals("UNSET")) {
			unset(param);
		} else if (cmd.equals("ECHO")) {
			echo(param);
		} else if (cmd.equals("DISCONNECT") || cmd.equals("BYE")
				|| cmd.equals("EXIT") || cmd.equals("QUIT")) {
			bye(param);
		} else if (cmd.equals("HELP") || cmd.equals("MAN")) {
			help(param);
		} else if (cmd.endsWith("CD") || cmd.endsWith("CHDIR")) {
			cd(param);
		} else if (cmd.equals("PWD")) {
			pwd();
		} else if (cmd.equals("MKDIR") || cmd.equals("MD")) {
			mkdir(param);
		} else if (cmd.equals("RMDIR") || cmd.equals("RD")) {
			rmdir(param);
		} else if (cmd.equals("RM") || cmd.equals("REMOVE")
				|| cmd.equals("DEL") || cmd.equals("DELETE")) {
			rm(param);
		} else if (cmd.equals("MV") || cmd.equals("MOVE")) {
			mv(param);
		} else if (cmd.equals("DIR") || cmd.equals("DIRECTORY")) {
			dir(param);
		} else if (cmd.equals("LS") || cmd.equals("LIST")) {
			list(param);
		} else if (cmd.equals("GET")) {
			get(param);
		} else if (cmd.equals("PULL")) {
			pull(param);
		} else if (cmd.equals("PUT")) {
			put(param);
		} else if (cmd.equals("CP") || cmd.equals("COPY")) {
			cp(param);
		} else if (cmd.equals("TOUCH")) {
			touch(param);
		} else if (cmd.equals("CAT") || cmd.equals("TYPE")
				|| cmd.equals("VIEW")) {
			cat(param);
		} else if (cmd.equals("DF")) {
			df(param);
		} else if (cmd.equals("DU")) {
			du(param);
		} else if (cmd.equals("HEAD")) {
			head(param);
		} else if (cmd.equals("FIND")) {
			find(param);
		} else if (cmd.equals("GREP")) {
			grep(param);
		} else if (cmd.equals("ENC") || cmd.equals("ENCODE")) {
			enc(param);
		} else if (cmd.equals("DEC") || cmd.equals("DECODE")) {
			dec(param);
		} else if (cmd.equals("ABORT")) {
			abort();
		} else if (cmd.equals("GRANT")) {
			grant(param);
		} else if (cmd.equals("REVOKE")) {
			revoke(param);
		} else if (cmd.equals("GROUP")) {
			group(param);
		} else if (cmd.equals("GROUPADD")) {
			groupadd(param);
		} else if (cmd.equals("GROUPMOD")) {
			groupmod(param);
		} else if (cmd.equals("GROUPDEL")) {
			groupdel(param);
		} else if (cmd.equals("USER")) {
			user(param);
		} else if (cmd.equals("USERADD")) {
			useradd(param);
		} else if (cmd.equals("USERMOD")) {
			usermod(param);
		} else if (cmd.equals("USERDEL")) {
			userdel(param);
		} else if (cmd.equals("MAIL")) {
			mail(param, data);
		} else if (cmd.equals("WHOAMI") || cmd.equals("ID")) {
			whoami(param);
		} else if (cmd.equals("WHO") || cmd.equals("W")) {
			who(param);
		} else if (cmd.equals("WRITE")) {
			write(param, data);
		} else if (cmd.equals("EDIT")) {
			edit(param, data);
		} else if (cmd.equals("UNMOUNT") || cmd.equals("UMOUNT")) {
			unmount(param);
		} else if (cmd.equals("MOUNT")) {
			mount(param);
		} else if (cmd.equals("LN") || cmd.equals("LINK")) {
			link(param);
		} else if (cmd.equals("UNLINK")) {
			unlink(param);
		} else if (cmd.equals("DATE")) {
			date(param);
		} else if (cmd.equals("PLAN") || cmd.equals("FINGER")) {
			plan(param);
		} else if (cmd.equals("WATCH")) {
			// watch(param);
		} else if (cmd.equals("UNWATCH")) {
			// unwatch(param);
		} else if (cmd.equals("NOTE")) {
			note(param);
		} else if (cmd.equals("NOTEADD")) {
			noteadd(param, data);
		} else if (cmd.equals("JAVAC")) {
			javac(param);
		} else if (cmd.equals("JAVA")) {
			java(param);
		} else if (cmd.equals("DEX")) {
			dex(param);
		} else if (cmd.equals("ANT")) {
			ant(param);
		} else if (cmd.equals("BSH")) {
			bsh(param, data);
		} else if (cmd.equals("CKSUM") || cmd.equals("CHECKSUM")) {
			cksum(param);
		} else if (cmd.equals("PING")) {
			ping(param);
		} else if (cmd.equals("THUMBNAIL")) {
			thumbnail(param);
		} else if (cmd.equals("ICON")) {
			icon(param);
		} else {
			ni(cmd, param);
		}
	}

	private void shell(String param) {
	}

	private void connect(String args) {
		ok("Welcome. "+shell.id());
	}

	protected void find(String args) throws Exception {
//		final String pa[] = split(args, 2);
//		if (pa.length < 2) {
//			err("Missing argument");
//			return;
//		}
//		final File dir = resolveFile(pa[0]);
//		final boolean verbose = shell.isVerbose();
//		final int[] count = new int[1];
//		count[0] = 0;
//		final Collector<File> out = new Collector<File>() {
//			public void add(File obj) {
//				print("" + obj);
//				count[0]++;
//			}
//		};
//		final Collector<File> trail = new Collector<File>() {
//			private static final long serialVersionUID = 1L;
//
//			public void add(File obj) {
//				if (verbose) {
//					print("> " + obj);
//				}
//			}
//		};
//		Thread th = new Thread() {
//			public void run() {
//				try {
//					//
//					ok("Finding. " + crlf);
//					shell.find(dir, pa[1], out, trail);
//					print("Total: " + count[0] + " files found in "
//							+ dir.getPath() + crlf);
//				} catch (Exception e) {
//					print(e.getMessage());
//				}
//			}
//		};
//		th.start();
	}

//	private void formatln(StringBuilder sb, boolean isDirectory,
//			boolean canRead, boolean canWrite, boolean canExecute, long length,
//			long modified, String name) {
//		format(sb, isDirectory, canRead, canWrite, canExecute, length,
//				modified, name);
//		sb.append(crlf);
//	}

	private void format(StringBuilder sb, boolean isDirectory, boolean canRead,
			boolean canWrite, boolean canExecute, long length, long modified,
			String name) {
		sb.append(isDirectory ? "d" : "-");
		sb.append(canRead ? "r" : "-");
		sb.append(canWrite ? "w" : "-");
		sb.append(canExecute ? "x" : "-");
		sb.append("\t");
		sb.append("?\t");
		sb.append("?\t");
		sb.append(length + "");
		sb.append("\t");
		try {
			sb.append(shell.format(new Date(modified)));
		} catch (Exception e) {
		}
		sb.append("\t");
		sb.append(name);
		sb.append(isDirectory ? "/" : "");
	}

	protected void get(String args) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("File name missing. get <filename> [peer]");
		} else {
			File f = resolveFile(pa[0]);
			String to = pa.length == 2 ? pa[1] : shell.getUser().getPeer();
			if (f.exists()) {
				send(to, f);
				return;
			} else {
				err("File does not exist. " + f.getPath());
			}
		}
	}

	protected void grant(String args) throws Exception {
		shell.ensureAdmin();
		// TODO move code to shell
		String[] pa = split(args, 2);
		Role.Type type = Role.toType(pa[0]);
		if (pa[0] == null || type == null || pa[1] == null) {
			StringBuffer sb = new StringBuffer();
			sb.append("200 Grantee list" + crlf);
			int cnt = 0;
			for (Role g : shell.getRoles()) {
				if (type == null || g.getType().equals(type)) {
					sb.append(g.getType());
					sb.append(nbsp);
					sb.append(g.getName());
					sb.append(crlf);
					cnt++;
				}
			}
			sb.append("Total: " + cnt);
			print(sb.toString());

		} else {
			// Role g = new Role(type, pa[1]);
			// roles.add(g);
			Role g = shell.grant(type, pa[1]);
			ok("Granted. " + g);
		}
	}

	protected void grep(String args) throws Exception {
//
	}

	/**
	 * List all groups or groups for a given user
	 * 
	 * @param args
	 * @throws Exception
	 */
	protected void group(String args) throws Exception {
		String[] pa = split(args, 1);

		if (pa.length == 0) {
			List<Group> groups = shell.groups();

			StringBuffer sb = new StringBuffer();
			sb.append("200 Group listing." + crlf);
			int cnt = 0;

			for (Group g : groups) {
				sb.append(g.getName());
				sb.append(crlf);
				cnt++;
			}
			sb.append("Total: " + cnt);
			print(sb.toString());
			return;
		} else {
			//
			String user = pa[0];
			List<Group> groups = shell.groups(user);

			StringBuffer sb = new StringBuffer();
			sb.append("200 Group listing for user. " + user + crlf);
			int cnt = 0;

			for (Group g : groups) {
				sb.append(g.getName());
				sb.append(crlf);
				cnt++;
			}
			sb.append("Total: " + cnt);
			print(sb.toString());
		}
	}

	protected void groupadd(String args) throws Exception {
		// groupadd name
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			err("Invalid command");
			return;
		}
		String group = pa[0];
		shell.addGroup(group);

		ok("Group added. " + group);
	}

	protected void groupdel(String args) throws Exception {
		// groupdel name
		String[] pa = split(args, 1);

		if (pa.length == 0) {
			err("Invalid command");
			return;
		}
		String group = pa[0];
		shell.removeGroup(group);

		ok("Group removed. " + group);
	}

	protected void groupmod(String args) throws Exception {
		// groupmod old new

		String[] pa = split(args, 2);
		if (pa.length != 2) {
			err("Invalid command");
			return;
		}

		String oldid = pa[0];
		String newid = pa[1];

		shell.updateGroup(oldid, newid);

		ok("Group name modified.");
	}

	protected void head(String args) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("Filename required");
		} else {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				int max = 10; // default
				try {
					max = Integer.parseInt(pa[1]);
				} catch (Exception e) {
				}
				// String s = fileman.readlines(f, max);
				ok("" + f.getPath() + crlf + shell.head(f, max));
				return;
			} else {
				err("File does not exist. " + f.getPath());
			}
		}
	}

	protected void help(String args) {
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			ok("List of commands. For more info on a specific command, type HELP command"
					+ crlf + Man.help(null));
		} else {
			ok("" + pa[0] + crlf + Man.help(pa[0]));
		}
	}

	protected void icon(String args) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("File name missing");
		} else {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				shell.icon(f, console.data());
				return;
			} else {
				err("File does not exist. " + f.getPath());
			}
		}
	}

	protected void info(String args) throws Exception {
		String[] pa = split(args, 1);
		File f = shell.resolveFile((pa.length > 0) ? pa[0] : null);
		if (f.exists()) {
			StringBuilder sb = new StringBuilder();
			shell.info(f, sb, crlf);
			sb.append(crlf);
			ok(sb.toString());
		} else {
			status("501", "File does not exist. " + f.getPath());
		}
	}

	protected void init() throws Exception {
		shell.init();
	}

	protected void java(String args) throws Exception {
		String[] pa = split(args, 1024);
		if (pa.length == 0) {
			err("Missing argument");
			return;
		}
		shell.java(pa);
	}

	protected void javac(String args) throws Exception {
		// shell.javac(src, outdir, logger);
	}

	protected void link(String args) throws Exception {
		// link file uri opt
		String[] pa = split(args, 3);
		if (pa.length < 2) {
			err("Missing argement");
			return;
		}
		String opt = pa.length < 3 ? "r" : pa[2];
		String ln = pa[0].endsWith(File.LINK_EXT) ? pa[0] : pa[0]
				+ File.LINK_EXT;
		File f = resolveFile(ln);
		shell.link(f, pa[1], opt);
		ok("Link created. " + f.getPath());
	}

	protected void list(String args) throws Exception {
		// pathname offset max
		String[] pa = split(args, 3);
		File f = resolveFile(pa);
		if (!f.exists()) {
			err("File does not exist. " + f);
			return;
		}
		int off = 0;
		int max = 10;
		try {
			switch (pa.length) {
			case 0:
			case 1:
				break;
			case 2:
				off = Integer.parseInt(pa[1]);
				max = 10;
				break;
			case 3:
				off = Integer.parseInt(pa[1]);
				max = Integer.parseInt(pa[2]);
				break;
			}
		} catch (Exception e) {
		}
		if (f.isDirectory()) {
			final StringBuilder sb = new StringBuilder();
//
//			Collector<File> out = new Collector<File>() {
//				public void add(File fi) {
//					formatln(sb, fi.isDirectory(), fi.canRead(), fi.canWrite(),
//							false, fi.length(), fi.lastModified(), fi.getName());
//				}
//			};
			int total = shell.list(f, off, max, sb);
			sb.append("Total: " + total + crlf);
			ok(f.getPath() + " " + off + " " + max
					+ crlf + sb.toString());
		} else {
//			String ext = f.getExt();
//			if (ext.equals("zip") || ext.equals("jar") || ext.equals("apk")) {
//				final StringBuilder sb = new StringBuilder();
//				final boolean canRead = f.canRead();
//				final boolean canWrite = f.canWrite();
//
//				Collector<ZipEntry> out = new Collector<ZipEntry>() {
//					public void add(ZipEntry ze) {
//						formatln(sb, ze.isDirectory(), canRead, canWrite,
//								false, ze.getSize(), ze.getTime(), ze.getName());
//					}
//				};
//				int total = shell.listZip(f, off, max, out);
//				sb.append("Total: " + total + crlf);
//				ok(f.getPath() + " " + off + " " + max
//						+ crlf + sb.toString());
//				return;
//			}
			err("Not a directory/zip");
		}
	}

//	protected void listc(String args) throws Exception {
//		// filename index
//		String[] pa = split(args, 2);
//		File f = resolveFile(pa);
//		if (!f.exists()) {
//			err("File does not exist");
//			return;
//		}
//		final StringBuilder sb = new StringBuilder();
//		shell.info(f, sb, crlf);
//		if (!f.isDirectory()) {
//			ok(sb.toString());
//			return;
//		}
//		//
//		int fileindex = 0;
//		try {
//			if (pa.length > 1) {
//				fileindex = Integer.parseInt(pa[1]);
//			}
//		} catch (Exception e) {
//		}
//		//
//		final int index = fileindex;
//		Collector<Index> indice = new Collector<Index>() {
//			int cnt = 0;
//
//			public void add(File.Index idx) {
//				cnt++;
//				if (index == idx.pos) {
//					sb.append("[" + idx.pos + ":" + idx.name + "]");
//				} else {
//					sb.append(idx.pos + ":" + idx.name);
//				}
//				sb.append(" ");
//				return;
//			}
//		};
//		Collector<File> out = new Collector<File>() {
//			int cnt = 0;
//
//			public void add(File file) {
//				cnt++;
//				try {
//					formatln(sb, file.isDirectory(), file.canRead(), file
//							.canWrite(), file.canExecute(), file.length(), file
//							.lastModified(), file.getName());
//				} catch (Exception e) {
//					e.printStackTrace();
//				}
//				return;
//			}
//		};
//
//		// indice
//		int total = shell.listi(f, pagesize , indice);
//		if (total < pagesize) {
//			sb.append("-"); // no index
//		}
//		// content
//		sb.append(crlf);
//		shell.list(f, fileindex, pagesize, out);
//		sb.append("Total: " + total + crlf);
//		ok(sb.toString());
//	}
//
//	protected void listp(String args) throws Exception {
//		String[] pa = split(args, 2);
//		File f = resolveFile(pa);
//		if (!f.exists()) {
//			err("File does not exist. " + f.getPath());
//			return;
//		}
//		Pattern pattern = pa.length > 1 ? Pattern.compile(pa[1]) : null;
//		if (f.isDirectory()) {
//
//			final StringBuilder sb = new StringBuilder();
//			final int[] cnt = new int[1];
//			Collector<File> out = new Collector<File>() {
//				public void add(File fi) {
//					formatln(sb, fi.isDirectory(), fi.canRead(), fi.canWrite(),
//							false, fi.length(), fi.lastModified(), fi.getName());
//					cnt[0]++;
//				}
//
//			};
//
//			int total = shell.listp(f, pattern, out);
//			sb.append("Total: " + total + " match: " + cnt[0] + crlf);
//			ok(sb.toString());
//		} else {
//			String ext = f.getExt();
//			if (ext.equals("zip") || ext.equals("jar") || ext.equals("apk")) {
//				final boolean canRead = f.canRead();
//				final boolean canWrite = f.canWrite();
//				final StringBuilder sb = new StringBuilder();
//
//				final int[] cnt = new int[1];
//				Collector<ZipEntry> out = new Collector<ZipEntry>() {
//					public void add(ZipEntry ze) {
//						formatln(sb, ze.isDirectory(), canRead, canWrite,
//								false, ze.getSize(), ze.getTime(), ze.getName());
//						cnt[0]++;
//					}
//
//				};
//				int total = shell.listZip(f, pattern, out);
//				sb.append("Total: " + total + " match: " + cnt[0] + crlf);
//				ok(sb.toString());
//				return;
//			}
//			err("Not a directory/zip");
//		}
//	}
//
	
	private void log(String s) {
		console.log(s);
	}

	protected void mail(String args, String data) throws Exception {
		// mail <recipient> <filename>
		String[] pa = split(args, 2);
		if (pa.length != 2) {
			err("Missing recipient or filenanme");
			return;
		}
		File f = null;

		String to = pa[0];
		f = resolveFile(pa[1]);

		if (f.isFile()) {
			// SENT ?
			shell.mail(to, f);
			ok("File mailed. " + f.getPath());
		} else {
			err("Sending directory not supported");
		}
	}

	protected void mkdir(String args) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			err("Missing argument");
			return;
		}
		File f = resolveFile(pa[0]);
		if (!f.exists()) {
			if (shell.mkdirs(f)) {
				ok("Created. " + f.getPath());
			} else {
				err("Failed to create. " + f.getPath());
			}
		} else {
			err("File/directory exists. " + f.getPath());
		}
	}

	protected void motd() throws Exception {
		String s = shell.motd();
		if (s != null) {
			ok("motd." + crlf + s);
		} else {
			ok("motd. No message of the day");
		}
	}

	protected void mount(String args) throws Exception {
		String[] pa = split(args, 3);
		if (pa.length < 2) {
			err("Missing argument");
			return;
		}
		String opt = pa.length < 3 ? "r" : pa[2];
		File f = resolveFile(pa[0]);
		shell.mount(f, pa[1], opt);
		ok("Mounted. " + args);

	}

	protected void mv(String args) throws Exception {
		// TODO across fs
		String pa[] = split(args, 2);

		if (pa.length == 2) {
			File fr = resolveFile(pa[0]);
			File to = resolveFile(pa[1]);
			if (to.isDirectory()) {
				to = new File(to, fr.getName());
			}
			if (fr.exists() && !to.exists()) {
				if (shell.move(fr, to)) {
					ok("Renamed. " + fr.getPath());
				} else {
					err("Failed to rename. " + fr.getPath());
				}
			} else {
				err("Failed. " + fr.getPath());
			}
		} else {
			err("Invalid command");
		}
	}

	protected void ni(String cmd, String args) {
		err(cmd + " not supported. Type help for a list of available commands.");
	}

	protected void note(String args) throws Exception {
		String[] pa = split(args, 1);
		File f = resolveFile(pa);
		if (!f.exists()) {
			err("Invalid path. " + f.getPath());
			return;
		}
		String p = f.getPath();
		ok("notes. " + p);
		NoteIterator it = shell.notes(f, crlf);
		StringBuilder sb = new StringBuilder();
		int cnt = 0;
		while (it.hasNext()) {
			Note n = it.next();
			sb.setLength(0);
			sb.append(n.date);
			sb.append(nbsp);
			sb.append(n.name);
			sb.append(nbsp);
			sb.append(n.author);
			sb.append(crlf);
			sb.append(n.body);
			sb.append(crlf);
			print(sb.toString());
			cnt++;
		}
		print("Total: " + cnt);
	}

	protected void noteadd(String args, String data) throws Exception {
		String[] pa = split(args, 1);
		File f = resolveFile(pa);
		if (!f.exists()) {
			err("Invalid path. " + f.getPath());
			return;
		}
		String p = f.getPath();
		if (data == null) {
			err("Missing note data");
			return;
		}
		shell.addNote(f, data);
		ok("Note added. " + p);
	}

	protected void ok(String s) {
		console.ok(s + crlf);
	}

	private void out(String s) {
		console.out(s + crlf);
	}

	public void parse(String req) {
		if (Log.DEBUG) {
			Log.log(this, "parse input: " + req);
		}
		try {
			if (req == null || req.length() == 0) {
				return;
			}
			String cmdline = null;
			String data = null;
			int idx = req.indexOf("\r\n");
			int tlen = 1;
			if (idx == -1) {
				idx = req.indexOf("\n");
				if (idx == -1) {
					idx = req.indexOf("\r");
				}
			} else {
				tlen = 2;
			}
			if (idx == -1) {
				cmdline = req;
			} else {
				cmdline = req.substring(0, idx);
				idx += tlen;
				if (req.length() > idx) {
					data = req.substring(idx);
				}
			}

			String cmd = null;
			String param = null;

			// parse command
			String[] sa = split(cmdline, 2);
			cmd = (sa.length == 0) ? null : sa[0].toUpperCase();
			param = (sa.length > 1) ? sa[1] : null;

			//
			if (cmd != null && cmd.length() > 0) {
				if (shell.isConnected()) {
					execute(cmd, param, data);
				}
			}
		} catch (Throwable e) {
			// e.printStackTrace();
			print(e);
		}
	}

	protected void ping(String args) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			err("Missing argument");
			return;
		}
		String peer = pa[0];
		boolean rc = shell.ping(peer);
		if (rc) {
			ok(peer + " is available");
		} else {
			ok(peer + " is not available");
		}
	}

	protected void plan(String args) throws Exception {
		String s = shell.plan();
		if (s != null) {
			ok("plan." + crlf + s);
		} else {
			ok("plan. No plan");
		}
	}

	protected void print(String s) {
		out(s);
	}

	private void print(Throwable e) {
		err(e.getMessage());
		// e.printStackTrace(); //TODO remove
		// StringWriter s = new StringWriter();
		// e.printStackTrace(new PrintWriter(s));
		// err(e.getMessage()+"\r\n"+s.toString());
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	protected void pull(String args) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("File name missing");
		} else {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				shell.pull(f, console.data());
				return;
			} else {
				err("File does not exist. " + f.getPath());
			}
		}
	}

	protected void put(String args) throws Exception {
		String pa[] = split(args, 1);
		if (pa.length == 0) {
			err("File name missing. put <filename>");
		} else {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				err("File exists. " + f.getPath());
				return;
			}
			File p = (File) f.getParentFile();
			if (!p.canWrite()) {
				err("Can't write to folder. " + p.getPath());
				return;
			}
			receive(f);
			return;
		}
	}

	protected void pwd() throws Exception {
		ok("" + shell.getCwd().getPath());
	}

	protected void receive(File f) throws Exception {
		User user = shell.getUser();
		Monitor mon = shell.receive(f);
		print("100 Accepting file from " + user.getPeer() + ". " + f.getPath());
		new MyMonitorThread(mon).start();
	}

	private File resolveFile(String path) throws Exception {
		return shell.resolveFile(path);
	}

	private File resolveFile(String[] pa) throws Exception {
		return shell.resolveFile(pa.length > 0 ? pa[0] : null);
	}

	protected void revoke(String args) throws Exception {
		shell.ensureAdmin();
		// TODO move code to shell
		String[] pa = split(args, 2);
		if (pa.length != 2) {
			err("Invalid command");
			return;
		}
		Role.Type type = Role.toType(pa[0]);
		if (type == null) {
			err("Invalid command");
			return;
		} else {
			Role g = new Role(type, pa[1]);
			Set<Role> roles = shell.getRoles();
			if (roles.contains(g)) {
				roles.remove(g);
				ok("Revoked. " + g);
			} else {
				err("Not found. " + g);
			}
		}
	}

	protected void rm(String args) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length > 0) {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				if (shell.delete(f)) {
					ok("Removed. " + f.getPath());
				} else {
					err("Failed to remove. " + f.getPath());
				}
			} else {
				err("File does not exist. " + f.getPath());
			}
		} else {
			err("Invalid command");
		}
	}

	protected void rmdir(String args) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length > 0) {
			File f = resolveFile(pa[0]);
			if (f.exists() && f.isDirectory()) {
				if (shell.delete(f)) {
					ok("Removed. " + f.getPath());
				} else {
					err("Failed to remove. " + f.getPath());
				}
			} else {
				err("File does not exist or is not a directory. " + f.getPath());
			}
		} else {
			err("Missing argument");
		}
	}

	protected void send(String user, File f) throws Exception {
		print("100 Sending file to " + user + ". " + f.getPath());
		Monitor mon = shell.send(user, f);
		if (mon != null) {
			new MyMonitorThread(mon).start();
		}
	}

	protected void set(String args) throws Exception {
		if (args == null) {
			ok("set. " + shell.env().toString());
			return;
		}
		String[] nv = args.split("=", 2);
		String n = null;
		String v = null;
		switch (nv.length) {
		case 0:
			ok("set. " + shell.env().toString());
			break;
		case 1:
			n = nv[0].trim();
			v = shell.getEnv(n);
			ok("set. " + n + " = " + (v == null ? "" : v));
			break;
		case 2:
			n = nv[0].trim();
			v = nv[1].trim();
			shell.setEnv(n, v);
			ok("set. " + n + " = " + v);
		}
	}

	public void setConsole(Console console) {
		this.console = console;
	}

	public void setShell(IShell shell) {
		this.shell = shell;
	}

	private String[] split(String s, int limit) {
		if (s == null) {
			return new String[0];
		}
		s = s.trim();
		if (s.length() == 0) {
			return new String[0];
		}
		String[] sa = s.split("\\s+", limit);
		return sa;
	}

	private void status(String code, String msg) {
		console.status(code, msg + crlf);
	}

	protected void thumbnail(String args) throws Exception {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("File name missing");
		} else {
			File f = resolveFile(pa[0]);
			if (f.exists()) {
				shell.thumbnail(f, console.data());
				return;
			} else {
				err("File does not exist. " + f.getPath());
			}
		}
	}

	protected void touch(String args) throws Exception {
		String[] pa = split(args, 1);
		File f = resolveFile(pa[0]);
		if (f.exists()) {
			if (shell.touch(f)) {
				ok("File date modified. " + f.getPath());
			} else {
				err("Failed to modify file date. " + f.getPath());
			}
		} else {
			if (f.createNewFile()) {
				ok("File created. " + f.getPath());
			} else {
				err("Failed to create. " + f.getPath());
			}
		}
	}

	protected void unlink(String args) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			err("Missing argument");
			return;
		}
		File f = resolveFile(pa[0]);
		if (f.isLink()) {
			String p = f.getPath();
			//
			shell.unlink(f);
			ok("Link removed. " + p);
		} else {
			ok("Not a link. " + f.getPath());
		}
	}

	protected void unmount(String args) throws Exception {
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			err("Missing argument");
			return;
		}
		shell.unmount(pa[0]);
		ok("Unmounted. " + pa[0]);
	}

	protected void unset(String args) {
		String[] pa = split(args, 1);
		if (pa.length == 0) {
			err("Missing argument");
			return;
		}
		unset(pa[0]);
		ok("unset. " + pa[0]);
	}

	/**
	 * List all users or users for a given group
	 * 
	 * @param args
	 * @throws Exception
	 */
	protected void user(String args) throws Exception {

		String[] pa = split(args, 1);
		if (pa.length == 0) {
			List<User> users = shell.users();

			StringBuffer sb = new StringBuffer();
			sb.append("200 User listing." + crlf);
			int cnt = 0;

			for (User e : users) {
				sb.append(e.getId());
				sb.append(nbsp);
				sb.append(e.getName());
				sb.append(crlf);
				cnt++;
			}
			sb.append("Total: " + cnt);
			print(sb.toString());

		} else {
			String group = pa[0];

			List<User> users = shell.users(group);

			StringBuffer sb = new StringBuffer();
			sb.append("200 User listing for group. " + group + crlf);
			int cnt = 0;
			for (User e : users) {
				sb.append(e.getId());
				sb.append(nbsp);
				sb.append(e.getName());
				sb.append(crlf);
				cnt++;
			}
			sb.append("Total: " + cnt);
			print(sb.toString());
		}
	}

	/*
	 * add user to a group
	 */
	protected void useradd(String args) throws Exception {
		// useradd group uid name
		String[] pa = split(args, 3);

		if (pa.length != 3) {
			err("Invalid command.");
			return;
		}

		String gid = pa[0];
		String uid = pa[1];
		String name = pa[2];

		shell.addUser(uid, name, gid);

		ok("Entry added.");
	}

	/**
	 * remove user from a group
	 * 
	 * @param args
	 * @throws Exception
	 */
	protected void userdel(String args) throws Exception {
		// userdel gid uid

		String[] pa = split(args, 2);

		if (pa.length != 2) {
			err("Invalid command.");
			return;
		}

		String gid = pa[0];
		String uid = pa[1];

		shell.removeUser(uid, gid);
		ok("User removed from group " + uid);
	}

	protected void usermod(String args) throws Exception {
		// usermod uid new-name
		String[] pa = split(args, 2);
		if (pa.length != 2) {
			err("Invalid command.");
			return;
		}

		String uid = pa[0];
		String name = pa[1];
		shell.updateUser(uid, name);
		ok("User name modified.");
	}

	protected void who(String args) {
		StringBuffer sb = new StringBuffer();
		try {
			List<User> users = shell.who();
			sb.append("200 User list. ");
			sb.append("user access since" + crlf);
			for (User u : users) {
				sb.append(u.getId());
				sb.append(nbsp);
				sb.append(shell.format(new Date(u.getLastAccess())));
				sb.append(nbsp);
				sb.append(shell.format(new Date(u.getConnectionTime())));
				sb.append(crlf);
			}
			sb.append("Users: " + users.size());
			sb.append(crlf);
			print(sb.toString());
		} catch (Exception e) {
			err("" + e);
		}
	}

	protected void whoami(String args) throws Exception {
		ok("ID: " + shell.id());
	}

	protected void write(String args, String data) {
		String pa[] = split(args, 2);
		if (pa.length == 0) {
			err("Recipient required.");
		} else {
			String msg = pa[1] == null ? "" : pa[1];
			try {
				msg = "Message from " + shell.getUser() + ": " + msg;
				if (data != null) {
					msg += crlf + data;
				}
				shell.write(pa[0], msg);
				ok("Message sent");
			} catch (Exception e) {
				err("" + e);
			}
		}
	}
}
